home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / sysutl / cpuid.asm < prev    next >
Assembly Source File  |  1986-05-09  |  19KB  |  627 lines

  1.     title    CPUID - Determine CPU & NDP Type
  2.     page    58,122
  3.     name    CPUID
  4.  
  5. ;
  6. ;    CPUID uniquely identifies each NEC & Intel CPU & NDP.
  7. ;
  8. ; Notes on program structure:
  9. ;
  10. ;    This program uses four segments, two classes, and one group.
  11. ;    It demonstrates a useful technique for programmers who generate
  12. ;    .COM programs.  In particular, it shows how to use segment
  13. ;    classes to re-order segments, and how to eliminate the linker's
  14. ;    warning message about the absence of a stack segment.
  15. ;
  16. ;    The correspondence between segments and classes is as follows:
  17. ;
  18. ;            Segment        Class
  19. ;            -------        -----
  20. ;            STACK        prog
  21. ;            DATA        data
  22. ;            MDATA        data
  23. ;            CODE        prog
  24. ;
  25. ;    The segments apprear in the above order in the program source
  26. ;    to avoid forward references in the CODE segment to labels in
  27. ;    the DATA/MDATA segments.  However, because the STACK segment
  28. ;    appears first in the file, it and all segments in the same
  29. ;    class are made contiguous by the linker.  Thus they precede
  30. ;    the DATA/MDATA segments in the resulting .COM file because
  31. ;    the latter are in a different class.  In this manner, although
  32. ;    DATA and MDATA precede CODE in the source file, their order
  33. ;    is swapped in the .COM file.  That way there is no need for
  34. ;    an initial skip over the data areas to get to the CODE
  35. ;    segment.  As a side benefit, declaring a STACK segment (as
  36. ;    the first segment in the source) also eliminates the linker's
  37. ;    warning about that segment missing.  Finally, all segments
  38. ;    are declared to be in the same group so the linker can properly
  39. ;    resolve offsets.
  40. ;
  41. ;    Note that if you re-assemble the code for any reason, it is
  42. ;    important to use an assembler later than the IBM version 1.0.
  43. ;    That version has a number of bugs including an annoying habit
  44. ;    of alphabetizing segment names in the .OBJ file.  If you use
  45. ;    IBM MASM 2.0, be sure to specify /S to order the segments
  46. ;    properly.
  47. ;
  48. ;    If the program reports results at variance with your knowledge
  49. ;    of the system, please contact the author.
  50. ;
  51. ; Environments tested in:
  52. ;
  53. ;            CPU Speed
  54. ;    System         in MHz        CPU        NDP
  55. ;    ------        ---------    ---        ---
  56. ;    IBM PC AT    6        Intel 80286    Intel 80287
  57. ;    IBM PC AT    9        Intel 80286    Intel 80287
  58. ;    IBM PC AT    6        Intel 80286    none
  59. ;    IBM PC AT    8.5        Intel 80286    none
  60. ;    IBM PC        4.77        Intel 8088    Intel 8087-3
  61. ;    IBM PC        4.77        Intel 8088*    Intel 8087-3
  62. ;    IBM PC XT    4.77        Intel 8088    none
  63. ;    IBM PC XT    4.77        Intel 8088    Intel 8087-3
  64. ;    IBM PC Portable    4.77        NEC V20        none
  65. ;    COMPAQ        4.77        Intel 8088    none
  66. ;    COMPAQ        4.77        NEC V20        none
  67. ;    AT&T PC 6300    8        Intel 8086    Intel 8087-2
  68. ;    AT&T PC 6300    8        NEC V30        Intel 8087-2
  69. ;    Tandy 2000    8        Intel 80186    none
  70. ;
  71. ;    * = faulty CPU
  72. ;
  73. ; Program structure:
  74. ;
  75. ;    Group PGROUP:
  76. ;    Stack   segment STACK, byte-aligned, stack,  class 'prog'
  77. ;    Program segment CODE,  byte-aligned, public, class 'prog'
  78. ;    Data    segment DATA,  byte-aligned, public, class 'data'
  79. ;    Data    segment MDATA, byte-aligned, public, class 'data'
  80. ;
  81. ; Assembly requirements:
  82. ;
  83. ;    Use MASM 1.25 or later.
  84. ;    With IBM's MASM 2.0 only, use /S to avoid alphabetizing the
  85. ;        segment names.
  86. ;    Use /r option to generate real NDP code.
  87. ;
  88. ;    MASM CPUID/r;            to convert .ASM to .OBJ
  89. ;    LINK CPUID;            to convert .OBJ to .EXE
  90. ;    EXE2BIN CPUID CPUID.COM        to convert .EXE to .COM
  91. ;    ERASE CPUID.EXE            to avoid executing .EXE
  92. ;
  93. ;    Note that the linker doesn't warn about a missing stack segment.
  94. ;
  95. ; Author:
  96. ;
  97. ;    Original code by:    Bob Smith    May 1985
  98. ;                Qualitas, Inc.
  99. ;                8314 Thoreau Dr.
  100. ;                Bethesda, MD   20817
  101. ;
  102. ;    Arthur Zachai suggested the technique to distinguish within the
  103. ;    808x and 8018x families by exploiting the difference in the
  104. ;    length of their pre-fetch instruction queues.
  105. ;
  106. ;    Published in PC Tech Journal - April 1986 - Vol 4 No 4
  107.  
  108.     subttl    Structures, Records, Equates, & Macros
  109.     page
  110. ARG_STR    struc
  111.     dw    ?            ; caller's bp
  112. ARG_OFF    dw    ?            ; caller's offset
  113. ARG_SEG    dw    ?            ;          segment
  114. ARG_FLG    dw    ?            ;          flags
  115. ARG_STR    ends
  116.  
  117. ; Record to define bits in the CPU's & NDP's flags' registers
  118.  
  119. CPUFLAGS record    RO:1,NT:1,IOPL:2,OF:1,DF:1,IF:1,TF:1,SF:1,ZF:1,R1:1,AF:1,R2:1,PF:1,R3:1,CF:1
  120.  
  121. NDPFLAGS record    R4:3,IC:1,RC:2,PC:2,IEM:1,R5:1,PM:1,UM:1,OM:1,ZM:1,DM:1,IM:1
  122.  
  123. ;    FLG_PIQL    Pre-fetch instruction queue length, 0 => 4-byte
  124. ;                                1 => 6-byte
  125. ;    FLG_08        Intel 808x
  126. ;    FLG_NEC        NEC V20 or V30
  127. ;    FLG_18        Intel 8018x
  128. ;    FLG_28        Intel 8028x
  129. ;    FLG_87        Intel 8087
  130. ;    FLG_287        Intel 80287
  131. ;
  132. ;    FLG_CERR    Faulty CPU
  133. ;    FLG_NERR    Faulty NDP switch setting
  134.  
  135. FLG    record    RSVD:9,FLG_NERR:1,FLG_CERR:1,FLG_NDP:2,FLG_CPU:3
  136.  
  137. ; CPU-related flags
  138.  
  139. FLG_PIQL    equ    001b shl FLG_CPU
  140. FLG_08        equ    000b shl FLG_CPU
  141. FLG_NEC        equ    010b shl FLG_CPU
  142. FLG_18        equ    100b shl FLG_CPU
  143. FLG_28        equ    110b shl FLG_CPU
  144.  
  145. FLG_8088    equ    FLG_08
  146. FLG_8086    equ    FLG_08 or FLG_PIQL
  147. FLG_V20        equ    FLG_NEC
  148. FLG_v30        equ    FLG_NEC or FLG_PIQL
  149. FLG_80188    equ    FLG_18
  150. FLG_80186    equ    FLG_18 or FLG_PIQL
  151. FLG_80286    equ    FLG_28 or FLG_PIQL
  152.  
  153. ; NDP-related flags
  154.  
  155. ;            00b shl FLG_NDP        Not Present
  156. FLG_87        equ    01b shl FLG_NDP
  157. FLG_287    equ    10b shl FLG_NDP
  158. BEL        equ    07h
  159. LF        equ    0ah
  160. CR        equ    0dh
  161. EOS        equ    '$'
  162.  
  163. POPFF    macro
  164.     local    L1,L2
  165.     jmp    short L2        ; skip over IRET
  166. L1:
  167.     iret                ; pop the cs & ip pushed below along
  168.                     ; with the flags, our original purpose
  169. L2:
  170.     push    cs            ; prepare for IRET by pushing cs
  171.     call    L1            ; push ip, jump to IRET
  172.     endm                ; POPFF macro
  173.  
  174. TAB    macro    TYP
  175.     push    bx            ; save for a moment
  176.     and    bx,mask FLG_&TYP    ; isolate flags
  177.     mov    cl,FLG_&TYP        ; shift amount
  178.     shr    bx,cl            ; shift to low-order
  179.     shl    bx,1            ; times two to index table of words
  180.     mov    dx,TYP&MSG_TAB[bx]    ; ds:dx => descriptive message
  181.     pop    bx            ; restore
  182.     mov    ah,09h            ; function code to display string
  183.     int    21h            ; request dos service
  184.     endm                ; TAB macro
  185.     page
  186. INT_VEC    segment at 0            ; start INT_VEC segment
  187.         dd    ?        ; pointer to INT 00h
  188. INT01_OFF    dw    ?        ; pointer to INT 01h
  189. INT01_SEG    dw    ?
  190. INT_VEC    ends                ; end INT_VEC segment
  191.  
  192. PGROUP    group    STACK,CODE,DATA,MDATA
  193.  
  194. ; The following segment both positions class 'prog' segments lower in
  195. ; memory than others so the first byte of the resulting .COM file is
  196. ; in the CODE segment, as well as satisfies the LINKer's need to have
  197. ; a stack segment.
  198.  
  199. STACK    segment    byte stack 'prog'    ; start STACK segment
  200. STACK    ends                ; end STACK segment
  201.  
  202. I11_REC    record    I11_PRN:2,I11_RSV1:2,I11_COM:3,I11_RSV2:1,I11_DISK:2,I11_VID:2,I11_RSV3:2,I11_NDP:1,I11_IPL:1
  203.  
  204. DATA    segment    byte public 'data'    ; start DATA segment
  205.     assume    ds:PGROUP
  206.  
  207. OLDINT01_VEC    label    dword        ; save area for original INT 01h handler
  208. OLDINT01_OFF    dw    ?
  209. OLDINT01_SEG    dw    ?
  210.  
  211. NDP_CW        label    word        ; save area for NDP control word
  212.         db    ?
  213. NDP_CW_HI    db    0        ; high byte of control word
  214. NDP_ENV        dw    7 dup(?)    ; save area for NDP environment
  215.  
  216. DATA    ends
  217.     subttl    Message Data Area
  218.     page
  219. MDATA    segment    byte public 'data'    ; start MDATA segment
  220.     assume    ds:PGROUP
  221.  
  222. MSG_START    db    'CPUID -- Version 1.0'
  223.         db    CR,LF,CR,LF,EOS
  224. MSG_8088    db    'CPU is an Intel 8088.'
  225.         db    CR,LF,EOS
  226. MSG_8086    db    'CPU is an Intel 8086.'
  227.         db    CR,LF,EOS
  228. MSG_V20        db    'CPU is an NEC V20.'
  229.         db    CR,LF,EOS
  230. MSG_V30        db    'CPU is an NEC V30.'
  231.         db    CR,LF,EOS
  232. MSG_80188    db    'CPU is an Intel 80188.'
  233.         db    CR,LF,EOS
  234. MSG_80186    db    'CPU is an Intel 80186.'
  235.         db    CR,LF,EOS
  236. MSG_UNK        db    'CPU is a maverick -- 80288??.'
  237.         db    CR,LF,EOS
  238. MSG_80286    db    'CPU is an Intel 80286.'
  239.         db    CR,LF,EOS
  240.  
  241. CPUMSG_TAB    label    word
  242.     dw    PGROUP:MSG_8088        ; 000 = Intel 8088
  243.     dw    PGROUP:MSG_8086        ; 001 = Intel 8086
  244.     dw    PGROUP:MSG_V20        ; 010 = NEC V20
  245.     dw    PGROUP:MSG_V30        ; 011 = NEC V30
  246.     dw    PGROUP:MSG_80188    ; 100 = Intel 80188
  247.     dw    PGROUP:MSG_80186    ; 101 = Intel 80186
  248.     dw    PGROUP:MSG_UNK        ; 110 = ?
  249.     dw    PGROUP:MSG_80286    ; 111 = Intel 80286
  250.  
  251. NDPMSG_TAB    label    word
  252.     dw    PGROUP:MSG_NDPX        ; 00 = No NDP
  253.     dw    PGROUP:MSG_8087        ; 01 = Intel 8087
  254.     dw    PGROUP:MSG_80287    ; 10 = Intel 80287
  255.  
  256. MSG_NDPX    db    'NDP is not present.'
  257.         db    CR,LF,EOS
  258. MSG_8087    db    'NDP is an Intel 8087.'
  259.         db    CR,LF,EOS
  260. MSG_80287    db    'NDP is an Intel 80287.'
  261.         db    CR,LF,EOS
  262.  
  263. CERRMSG_TAB    label    word
  264.     dw    PGROUP:MSG_CPUOK    ; 0 = CPU healthy
  265.     dw    PGROUP:MSG_CPUBAD    ; 1 = CPU faulty
  266.  
  267. MSG_CPUOK    db    'CPU appears to be healthy.'
  268.         db    CR,LF,EOS
  269. MSG_CPUBAD    label    byte
  270.         db    BEL,'*** CPU incorrectly allows interrupts '
  271.         db    'after a change to SS ***',CR,LF
  272.         db    'It should be replaced with a more recent '
  273.         db    'version as it could crash the',CR,LF
  274.         db    'system at seemingly random times.',CR,LF,EOS
  275.  
  276. NERRMSG_TAB    label    word
  277.     dw    PGROUP:MSG_NDPSWOK    ; 0 = NDP switch set correctly
  278.     dw    PGROUP:MSG_NDPSWERR    ; 1 = NDP switch set incorrectly
  279.  
  280. MSG_NDPSWOK    db    EOS        ; no message
  281. MSG_NDPSWERR    label    byte
  282.         db    '*** Although there is an NDP installed '
  283.         db    'on this sytem, the corresponding',CR,LF
  284.         db    'system board switch is not properly set.  '
  285.         db    'To correct this, flip switch 2 of',CR,LF
  286.         db    'switch block 1 on the system board.',CR,LF,EOS
  287.  
  288. MDATA    ends                ; end MDATA segment
  289.     subttl    Main Routine
  290.     page
  291. CODE    segment    byte public 'prog'    ; start CODE segment
  292.     assume    cs:PGROUP,ds:PGROUP,es:PGROUP
  293.     org    100h            ; skip over PSP
  294.  
  295. INITIAL    proc    near
  296.     mov    dx,offset ds:MSG_START    ; starting message
  297.     mov    ah,09h            ; function code to display string
  298.     int    21h            ; request DOS service
  299.  
  300.     call    CPUID            ; check the CPU's identity
  301.  
  302.     TAB    CPU            ; display CPU results
  303.     TAB    NDP            ; display NDP results
  304.     TAB    CERR            ; display CPU ERR results
  305.     TAB    NERR            ; display NDP ERR results
  306.  
  307.     ret                ; return to DOS
  308. INITIAL    endp                ; end INITIAL procedure
  309.     subttl    CPUID Procedure
  310.     page
  311. CPUID    proc    near            ; start CPUID procedure
  312.     assume    cs:PGROUP,ds:PGROUP,es:PGROUP
  313.  
  314. ; This procedure determines the type of CPU and NDP (if any) in use.
  315. ;
  316. ; The possibilities include:
  317. ;
  318. ;        Intel 8086
  319. ;        Intel 8088
  320. ;        NEC V20
  321. ;        NEC V30
  322. ;        Intel 80186
  323. ;        Intel 80188
  324. ;        Intel 80286
  325. ;        Intel 8087
  326. ;        Intel 80287
  327. ;
  328. ; Also checked is whether or not the CPU allows interrupts after
  329. ; changing the SS register segment.  If the CPU does, it is faulty
  330. ; and should be replaced.
  331. ;
  332. ; Further, if an NDP is installed, non-AT machines should have a
  333. ; system board switch set.  Such a discrepancy is reported.
  334. ;
  335. ; On exit, BX contains flag settings (as defined in FLG record) which
  336. ; the caller can check.  For example, to test for an Intel 80286, use
  337. ;
  338. ;        and    bx,mask FLAG_CPU
  339. ;        cmp    bx,FLG_80286
  340. ;        je    ITSA286
  341.  
  342.     irp    XX,<ax,cx,di,ds,es>    ; save registers
  343.     push    XX
  344.     endm
  345.  
  346. ; test for 80286 -- this CPU executes PUSH SP by first storing SP on
  347. ; stack, then decrementing it.  earlier CPU's decrement, THEN store.
  348.  
  349.     mov    bx,FLG_28        ; assume it's a 286
  350.     push    sp            ; only 286 pushes pre-push SP
  351.     pop    ax            ; get it back
  352.     cmp    ax,sp            ; check for same
  353.     je    CHECK_PIQL        ; they are, so it's a 286
  354.  
  355. ; test for 80186/80188 -- 18xx and 286 CPU's mask shift/rotate
  356. ; operations mod 32; earlier CPUs use all 8 bits of CL.
  357.  
  358.     mov    bx,FLG_18        ; assume it's an 8018x
  359.     mov    cl,32+1            ; 18x masks shift counts mod 32
  360.                     ; note we can't use just 32 in CL
  361.     mov    al,0ffh            ; start with all bits set
  362.  
  363.     shl    al,cl            ; shift one position if 18x
  364.     jnz    CHECK_PIQL        ; some bits still on,
  365.                     ; so its a 18x, check PIQL
  366.  
  367. ; test for V20
  368.  
  369.     mov    bx,FLG_NEC        ; assume it's an NEC V-series CPU
  370.     call    CHECK_NEC        ; see if it's an NEC chip
  371.     jcxz    CHECK_PIQL        ; good guess, check PIQL
  372.  
  373.     mov    bx,FLG_08        ; it's an 808x
  374.     subttl    Check Length of Pre-Fetch Instruction Queue
  375.     page
  376. ; Check the length of the pre-fetch instruction queue (PIQ).
  377. ;
  378. ; xxxx6 CPUs have a PIQ length of 6 bytes,
  379. ; xxxx8 CPUs have a PIQ length of 4 bytes
  380. ;
  381. ; Self-modifying code is used to distinguish the two PIQ lengths.
  382.  
  383. CHECK_PIQL:
  384.     call    PIQL_SUB        ; handle via subroutine
  385.     jcxz    CHECK_ERR        ; if CX is 0, INC was not executed,
  386.                     ; hence PIQ length is 4
  387.     or    bx,FLG_PIQL        ; PIQ length is 6
  388.     subttl    Check for Allowing Interrupts After POP SS
  389.     page
  390. ; Test for faulty chip (allows interrupts after change to SS register)
  391.  
  392. CHECK_ERR:
  393.     xor    ax,ax            ; prepare to address
  394.                     ; interrupt vector segment
  395.     mov    ds,ax            ; DS points to segment 0
  396.     assume    ds:INT_VEC        ; tell the assembler
  397.  
  398.     cli                ; nobody move while we swap
  399.  
  400.     mov    ax,offset cs:INT01    ; point to our own handler
  401.     xchg    ax,INT01_OFF        ; get and swap offset
  402.     mov    OLDINT01_OFF,ax        ; save to restore later
  403.  
  404.     mov    ax,cs            ; our handler's segment
  405.     xchg    ax,INT01_SEG        ; get and swap segment
  406.     mov    OLDINT01_SEG,ax        ; save to restore later
  407.  
  408. ; note we continue with interrupts disabled to avoid
  409. ; an external interrupt occuring during this test
  410.  
  411.     mov    cx,1            ; initialize a register
  412.     push    ss            ; save ss to store back into itself
  413.     pushf                ; move flags
  414.     pop    ax            ; ... into ax
  415.     or    ax,mask TF        ; set trap flag
  416.     push    ax            ; place onto stack
  417.     POPFF                ; ... and then into effect
  418.                     ; some CPUs effect the trap flag
  419.                     ; immediately, some
  420.                     ; wait one instruction
  421.     nop                ; allow interrupt to take effect
  422.  
  423. POST_NOP:
  424.     pop    ss            ; change the stack segment register
  425.                     ; (to itself)
  426.     dec    cx            ; normal cpu's execute this instruction
  427.                     ; before recognizing the single-step
  428.                     ; interrupt
  429.     hlt                ; we never get here
  430.  
  431. INT01:
  432.  
  433. ; Note: IF=TF=0
  434. ; If we're stopped at or before POST_NOP, continue on
  435.  
  436.     push    bp            ; prepare to address the stack
  437.     mov    bp,sp            ; hello, Mr. stack
  438.  
  439.     cmp    [bp].ARG_OFF,offset cs:POST_NOP    ; check offset
  440.     pop    bp            ; restore
  441.     ja    INTO1_DONE        ; we're done
  442.  
  443.     iret                ; return to caller
  444.  
  445. INTO1_DONE:
  446.  
  447. ; restore old INT 01h handler
  448.  
  449.     les    ax,OLDINT01_VEC    ; ES:AX ==> old INT 01h handler
  450.     assume    es:nothing        ; tell the assembler
  451.     mov    INT01_OFF,ax        ; restore offset
  452.     mov    INT01_SEG,es        ; ... and segment
  453.     sti                ; allow interrupts again (IF=1)
  454.  
  455.     add    sp,3*2            ; strip ip, cs, and flags from stack
  456.  
  457.     push    cs            ; setup ds for code below
  458.     pop    ds
  459.     assume    ds:PGROUP        ; tell the assembler
  460.  
  461.     jcxz    CHECK_NDP        ; if cx is 0, the dec cx was executed,
  462.                     ; and the cpu is ok
  463.     or    bx,mask FLG_CERR    ; it's a faulty chip
  464.     subttl    Check For Numeric Data Processor
  465.     page
  466. ; Test for a Numeric Data Processor -- Intel 8087 or 80287.  The
  467. ; technique used is passive -- it leaves the NDP in the same state in
  468. ; which it is found.
  469.  
  470. CHECK_NDP:
  471.     cli                ; protect FNSTENV
  472.     fnstenv NDP_ENV            ; if NDP present, save
  473.                     ; current environment,
  474.                     ; otherwise, this instruction
  475.                     ; is ignored
  476.     mov    cx,50/7            ; cycle this many times
  477.     loop    $            ; wait for result to be stored
  478.     sti                ; allow interrupts
  479.     fninit                ; initialize processor to known state
  480.     jmp    short $+2        ; wait for initialization
  481.  
  482.     fnstcw    NDP_CW            ; save control word
  483.     jmp    short $+2        ; wait for result to be stored
  484.     jmp    short $+2
  485.     cmp    NDP_CW_HI,03h        ; check for NDP initial control word
  486.     jne    CPUID_EXIT        ; no NDP installed
  487.     int    11h            ; get equipment flags into ax
  488.     test    ax,mask I11_NDP        ; check NDP-installed bit
  489.     jnz    CHECK_NDP1        ; it's correctly set
  490.     or    bx,mask FLG_NERR    ; mark as in error
  491. CHECK_NDP1:
  492.     and    NDP_CW,not mask IEM    ; enable interrupts
  493.                     ; (IEM=0, 8087 only)
  494.     fldcw    NDP_CW            ; reload control word
  495.     fdisi                ; disable interrupts (IEM=1) on 8087,
  496.                     ; ignored by 80287
  497.     fstcw    NDP_CW            ; save control word
  498.     fldenv    NDP_ENV            ; restore original NDP environment
  499.                     ; no need to wait
  500.                     ; for environment to be loaded
  501.     test    NDP_CW,mask IEM        ; check interrupt enable mask
  502.                     ; (8087 only)
  503.     jnz    CPUID_8087        ; it changed, hence NDP is an 8087
  504.     or    bx,FLG_287        ; NDP is an 80287
  505.     jmp    short CPUID_EXIT    ; exit with falgs in BX
  506. CPUID_8087:
  507.     or    bx,FLG_87        ; NDP is an 8087
  508. CPUID_EXIT:
  509.     irp    XX,<es,ds,di,cx,ax>    ; restore registers
  510.     pop    XX
  511.     endm
  512.     assume    ds:nothing,es:nothing
  513.     ret                ; return to caller
  514. CPUID    endp                ; end CPUID procedure
  515.     subttl    Check For NEC V20/V30
  516.     page
  517. CHECK_NEC    proc    near
  518.  
  519. ; The NEC V20/V30 are very compatible with the Intel 8086/8088.
  520. ; The only point of "incompatibility" is that they do not contain
  521. ; a bug found in the Intel CPU's.  Specifically, the NEC CPU's
  522. ; correctly restart an interrupted multi-prefix string instruction
  523. ; at the start of the instruction.  The Intel CPU's incorrectly
  524. ; restart in the middle of the instruction.  This routine tests
  525. ; for that situation by executing such an instruction for a
  526. ; sufficiently long period of time for a timer interrupt to occur.
  527. ; If at the end of the instruction, CX is zero, it must be an NEC
  528. ; CPU; if not, it's an Intel CPU.
  529. ;
  530. ; Note that we're counting on the timer interrupt to do its thing
  531. ; every 18.2 times per second.
  532. ;
  533. ; Here's a worst case analysis: An Intel 8088/8086 executes 65535
  534. ; iterations of LODSB ES[SI] in 2+9+13*65535 = 851,966 clock ticks.
  535. ; If the Intel 8088/8086 is running at 10 MHz, each clock tick is
  536. ; 100 nanoseconds, hence the entire operation takes 85 milliseconds.
  537. ; If the timer is running at normal speed, it interrupts the CPU every
  538. ; 55ms and so should interrupt the repeated string instruction at least
  539. ; once.
  540.  
  541.     mov    cx,0ffffh        ; move a lot of data
  542.     sti                ; ensure timer enabled
  543.  
  544. ; execute multi-prefix instruction.  note that the value of ES as
  545. ; well as the direction flag setting is irrelevant.
  546.  
  547.     push    ax            ; save registers
  548.     push    si
  549.     rep    lods    byte ptr es:[si]
  550.     pop    si            ; restore
  551.     pop    ax
  552.  
  553. ; on exit: if cx is zero, it's an NEC CPU, otherwise it's an Intel CPU
  554.  
  555.     ret                ; return to caller
  556. CHECK_NEC    endp
  557.     subttl    Pre-Fetch Instruction Queue Subroutine
  558.     page
  559. PIQL_SUB    proc    near
  560.  
  561. ; This subroutine discerns the length of the CPU's pre-fetch
  562. ; instruction queue (PIQ).
  563. ;
  564. ; The technique used is to first ensure that the PIQ is full, then
  565. ; change an instruction which should be in a 6-byte PIQ but not in a
  566. ; 4-byte PIQ.  Then, if the original instruction is executed, the PIQ
  567. ; is 6-bytes long; if the new instruction is executed, PIQ length is 4.
  568. ;
  569. ; We ensure the PIQ is full be executing an instruction which takes
  570. ; long enough so that the Bus Interface Unit (BIU) can fill the PIQ
  571. ; while the instruction is executing.
  572. ;
  573. ; Specifically, for all byt the last STOSB, we're simple marking time
  574. ; waiting for the BIU to fill the PIQ.  The last STOSB actually changes
  575. ; the instruction.  By that time, the orignial instruction should be in
  576. ; a six-byte PIQ byt not a four-byte PIQ.
  577.  
  578.     assume    cs:PGROUP,es:PGROUP
  579. @REP    equ    3            ; repeat the store this many times
  580.     std                ; store backwards
  581.     mov    di,offset es:LAB_INC+@REP-1    ; change the instructions
  582.                     ; at ES:DI
  583.                     ; and preceding
  584.     mov    al,ds:LAB_STI        ; change to a sti
  585.     mov    cx,@REP            ; give the BIU time
  586.                     ; to pre-fetch instructions
  587.     cli                ; ensure interrupts are disabled,
  588.                     ; otherwise a timer tick
  589.                     ; could change the PIQ filling
  590.     rep    stosb            ; change the instruction
  591.                     ; during execution of this instruction
  592.                     ; the BIU is refilling the PIQ.  The
  593.                     ; current instruction is no longer
  594.                     ; in the PIQ.
  595.                     ; Note at end, CX is 0.
  596.  
  597. ; The PIQ begins filling here
  598.  
  599.     cld                ; restore direction flag
  600.     nop                ; PIQ fillers
  601.     nop
  602.     nop
  603.  
  604. ; The following instruction is beyond a four-byte-PIQ CPU's reach,
  605. ; but within that of a six-byte-PIQ CPU.
  606.  
  607. LAB_INC        label    byte
  608.     inc    cx            ; executed only if PIQ length is 6
  609.  
  610. LAB_STI    label     byte
  611.     rept    @REP-1
  612.     sti                ; restore interrupts
  613.     endm
  614.     ret                ; return to caller
  615.     assume    ds:nothing,es:nothing
  616. PIQL_SUB    endp            ; end PIQL_SUB procedure
  617.  
  618. CODE    ends                ; end code segment
  619.  
  620.     if1
  621. %OUT    Pass 1 Complete
  622.     else
  623. %OUT    Pass 2 Complete
  624.     endif
  625.  
  626.     end    INITIAL            ; end CPUID module
  627.